package rothag.models;

import java.util.ArrayList;
import java.util.Date;
import java.util.Random;
import java.util.UUID;
import rothag.models.developpement.*;
import rothag.enums.EtapeJeu;
import rothag.models.monuments.*;

/**
 * Implémentation de la partie
 * @author gulian.lorini
 * @version 0.1
 */
public class Partie extends ModelBase implements PartieInterface {

    private int tour;
    private JoueurInterface joueurCourant;
    private ArrayList<JoueurInterface> listeJoueur;
    private EtapeJeu etapeCourante;
    private Date debutPartie;
    private Date derniereSauvegarde;
    private UUID identifiant;
    private ArrayList<Monument> listeMonumentPossible;
    private ArrayList<Developpement> listeDeveloppementPossible;
    private ArrayList<String> listeActionsJoueurCourant;

    /**
     * Constructeur à partir d'une liste de noms
     * @param listeNom Liste des noms des joueurs de la partie
     * @throws Liste des noms invalide
     */
    public Partie(ArrayList<String> listeNom) throws Exception {

        // Vérification des paramètres
        if (listeNom == null || listeNom.isEmpty()) {
            throw new Exception("Impossible de créer un nouvelle partie : la liste des noms n'est pas valide.");
        }

        int i = 0;
        String[] listeNomAlea = new String[listeNom.size()];
        Random rand = new Random();

        // Tirage aléatoire des joueurs
        while (listeNom.size() > 0) {
            int alea = Math.abs(rand.nextInt()) % listeNom.size();
            listeNomAlea[i] = listeNom.get(alea);
            listeNom.remove(alea);
            i++;
        }


        // Création de la liste de monuments possibles
        listeMonumentPossible = new ArrayList<Monument>();
        listeMonumentPossible = creerListeMonumentDisponible(listeNom.size());

        // Création de la liste des developpements possibles
        listeDeveloppementPossible = new ArrayList<Developpement>();
        listeDeveloppementPossible = creerListeDeveloppement();

        // Création de chaque joueur
        listeJoueur = new ArrayList<JoueurInterface>(listeNomAlea.length);

        for (String nom : listeNomAlea) {
            listeJoueur.add(new Joueur(nom, listeMonumentPossible));
        }

        tour = 1;
        debutPartie = new Date();
        identifiant = UUID.randomUUID();

        // Liste des actions du tours
        listeActionsJoueurCourant = new ArrayList<String>();
    }

    /**
     * Retourn le tour de la partie
     * @return Tour en cours
     */
    public int getTour() {
        return tour;
    }

    /**
     * Met à jour le tour de la partie
     * @param tour Nouvelle valeur
     */
    public void setTour(int tour) {
        this.tour = tour;
    }

    /**
     * Retourne le joueur courant
     * @return Joueur courant de la partie
     */
    public JoueurInterface getJoueurCourant() {
        return joueurCourant;
    }

    /**
     * Met à jour le joueur courant
     * @param joueurCourant Nouveau joueur
     */
    public void setJoueurCourant(JoueurInterface joueurCourant) {
        this.joueurCourant = joueurCourant;
    }

    /**
     * Retourne la liste des joueurs
     * @return Liste de joueurs
     */
    public ArrayList<JoueurInterface> getListeJoueur() {
        return listeJoueur;
    }

    /**
     * Retourne l'étape courante de la partie
     * @return Etape du jeu
     */
    public EtapeJeu getEtapeCourante() {
        return etapeCourante;
    }

    /**
     * Met à jour l'étape courante de la partie
     * @param etapeCourante Nouvelle valeur
     */
    public void setEtapeCourante(EtapeJeu etapeCourante) {
        this.etapeCourante = etapeCourante;
    }

    /**
     * Retourne la date de début de la partie
     * @return Date de début
     */
    public Date getDebutPartie() {
        return debutPartie;
    }

    /**
     * Retourne la date de dernière sauvegarde
     * @return Date de dernière sauvegarde
     */
    public Date getDerniereSauvegarde() {
        return derniereSauvegarde;
    }

    /**
     * Met à jour la date de dernière sauvegarde
     * @param derniereSauvegarde Nouvelle valeur
     */
    public void setDerniereSauvegarde(Date derniereSauvegarde) {
        this.derniereSauvegarde = derniereSauvegarde;
    }

    /**
     * Retourne l'identifiaant de la partie
     * @return Identifiant
     */
    public UUID getIdentifiant() {
        return identifiant;
    }

    /**
     * Création de la liste des monuments possibles en fonction du nombre de joueurs
     * @param nbJoueur Nombre de joueur de la partie
     * @return Liste de monuement
     */
    private ArrayList<Monument> creerListeMonumentDisponible(int nbJoueur) {

        ArrayList<Monument> mon = new ArrayList<Monument>();

        // Création des monuments présents quelque soit le nombre de joueurs
        mon.add(new PetitePyramide());
        mon.add(new Stonehenge());
        mon.add(new Obelisque());
        mon.add(new GrandeMuraille());

        // Création des monument en fonction du nombre de joueurs
        switch (nbJoueur) {
            case 2:
                mon.add(new JardinsSuspendus());
                break;
            case 3:
                mon.add(new Temple());
                mon.add(new GrandePyramide());
                break;
            default:
                mon.add(new JardinsSuspendus());
                mon.add(new Temple());
                mon.add(new GrandePyramide());
                break;
        }

        return mon;
    }

    /**
     * Création de la liste des développements possibles
     * @return Liste de développements
     */
    private ArrayList<Developpement> creerListeDeveloppement() {
        ArrayList<Developpement> dev;

        dev = new ArrayList<Developpement>();

        // A laisser par ordre de coût
        dev.add(new Conduite());
        dev.add(new Irrigation());
        dev.add(new Agriculture());
        dev.add(new Carriere());
        dev.add(new Medecine());
        dev.add(new Finance());
        dev.add(new Caravanes());
        dev.add(new Conservation());
        dev.add(new Forge());
        dev.add(new Navigation());
        dev.add(new Religion());
        dev.add(new Grenier());
        dev.add(new Maconnerie());
        dev.add(new Ingenierie());
        dev.add(new Commerce());
        dev.add(new Architecture());
        dev.add(new Empire());

        return dev;
    }

    /**
     * Retourne un développement en fonction de sa classe
     * @param developpement Classe du développement cherché
     * @return Un développement
     */
    public Developpement chercheDeveloppement(Class developpement) {
        Developpement retour = null;

        for (Developpement d : listeDeveloppementPossible) {
            if (d.getClass() == developpement) {
                retour = d;
                break;
            }
        }
        return retour;
    }

    /**
     * Retourne la liste des développements possibles
     * @return Liste de développements
     */
    public ArrayList<Developpement> getlisteDeveloppementPossible() {
        return this.listeDeveloppementPossible;
    }

    /**
     * Retourne la liste de joueurs sous forme de String
     * @return Liste des joueurs
     */
    public String getNomsJoueur() {
        StringBuilder joueurs = new StringBuilder();
        for (int k = 0; k < listeJoueur.size(); k++) {
            if (k != 0) {
                if (k < listeJoueur.size() - 1) {
                    joueurs.append(", ");
                } else {
                    joueurs.append(" et ");
                }
            }
            joueurs.append(listeJoueur.get(k).getNom());
        }
        return joueurs.toString();
    }

    /**
     * Retourne la liste des actions du joueur courant
     * @return Liste de String
     */
    public ArrayList<String> getListeActionsJoueurCourant() {
        return listeActionsJoueurCourant;
    }

    /**
     * Détermine si la partie est finie
     * @return Vrai si la partie est terminée
     */
    public boolean estTermine() {
        boolean retour = false;

        if (listeJoueur.size() == 1) { // Partie solo
            // La partie est terminée au 10 éme tour
            retour = (tour == 10);
        } else { //Partie multijoueur
            if (this.getJoueurCourant() == listeJoueur.get(listeJoueur.size() - 1)) {
                // Permière vérification, un joueur a 7 développements
                for (JoueurInterface j : listeJoueur) {
                    if (j.getListeDeveloppements().size() >= 7) {
                        retour = true;
                        break;
                    }
                }
                if (!retour) {
                    // Seconde vérification, tous les joueurs ont construit les monuments
                    retour = true;
                    for (JoueurInterface j : listeJoueur) {
                        int nbMonument = 0;
                        // Calcul du nombre de muments réalisés
                        for (MonumentJoueurInterface m : j.getListeMonumentJoueur()) {
                            if (!m.getRealise()) {
                                break;
                            }
                            nbMonument++;
                        }
                        // Vérification que tous les monuements soient réalisés
                        if (nbMonument != listeMonumentPossible.size()) {
                            retour = false;
                            break;
                        }
                    }
                }
            }
        }

        return retour;
    }
}
